package com.navinfo.platform.trip.analysis.service;

import com.navinfo.location.mileage.bean.MileageAndFuel;
import com.navinfo.platform.trip.analysis.pojo.PreTripInfo;
import com.navinfo.platform.trip.analysis.pojo.TripJudgeState;
import com.navinfo.platform.trip.analysis.util.TripStatisticUtils;
import com.navinfo.platform.trip.common.arithmetic.ParkingPowerOn;
import com.navinfo.platform.trip.common.arithmetic.data.DrivingData;
import com.navinfo.platform.trip.common.arithmetic.var.VarParkingPowerOn;
import com.navinfo.platform.trip.common.enums.ProtocolEnum;
import com.navinfo.platform.trip.common.enums.RunningTypeEnum;
import com.navinfo.platform.trip.common.enums.SystemTypeEnum;
import com.navinfo.platform.trip.common.enums.TripStatusEnum;
import com.navinfo.platform.trip.common.pojo.PointProtocol;
import com.navinfo.platform.trip.common.util.ArithmeticUtils;
import com.navinfo.platform.trip.common.util.CommonUtils;
import org.apache.flink.streaming.runtime.operators.windowing.TimestampedValue;

import java.io.Serializable;
import java.util.*;

import static com.navinfo.platform.trip.common.enums.TripStatusEnum.*;

/**
 * 多车厂实现：行程切分服务
 * @author: web
 * @data: 2020-04-12
 */
public interface TripJudgeService extends Serializable {
    /**
     * 强制结束行程时长阀值：10分钟
     * 单位:MS
     */
    long FORCE_END_DURATION = 10 * 60 * 1000L;
    /**
     * 行程最少的0200点数量
     */
    int TRIP_MIN_COUNT = 2;

    /**
     * 获取系统类型
     */
    SystemTypeEnum systemType();

    /**
     * 判断当前行程状态
     * @param tripJudgeState 行程状态判断的缓存
     * @param point 当前点
     * @return 当前车辆行程状态
     */
    TripStatusEnum tripStatus(TripJudgeState tripJudgeState, PointProtocol point);

    /**
     * 根据0200切分行程
     * 1）根据0200点，切分出子行程，设置行程开始、结束信息；<br/>
     * 2）添加行程内的0200和0F37点集合;<br/>
     * 3）根据0200的子行程调用精准里程油耗算法算出子行程的精准里程、精准油耗<br/>
     * 4）处理行程外的统计信息，目前支持停车通电算法
     * @param points 排完序的0f37和0200混合点
     * @param drivingData 算法链的缓存信息
     * @param runningType 运行类型
     * @return 流计算返回第一个行程，批计算返回所有行程列表
     */
    default <T> List<PreTripInfo> judgeTrip(Iterable<T> points, DrivingData drivingData, RunningTypeEnum runningType) {
        List<PreTripInfo> tripInfoList = new ArrayList<>();
        if (null != points) {
            PreTripInfo preTripInfo = null;
            TripJudgeState tripJudgeState = null;

            for (T t: points) {
                //流计算只取第一个行程
                if(RunningTypeEnum.STREAM.equals(runningType) && tripInfoList.size()>0){
                    return  Collections.singletonList(tripInfoList.get(0));
                }

                PointProtocol point;
                if(t instanceof PointProtocol){
                    point = (PointProtocol) t;
                }else if(t instanceof TimestampedValue){
                    point = (PointProtocol) ((TimestampedValue) t).getValue();
                }else {
                    break;
                }

                //根据0200点切分行程
                if(ProtocolEnum.LOCATION.equals( point.getProtocol())){
                    if(! validate(point, null==tripJudgeState ? 0 : (tripJudgeState.getLastGpsTime()==null ? 0 : tripJudgeState.getLastGpsTime()))){
                        continue;
                    }

                    TripStatusEnum tripStatusEnum = tripStatus(tripJudgeState, point);
                    if(TRIP_NO_START.equals(tripStatusEnum)){
                        //行程外统计信息处理
                        if(null==preTripInfo && null!=drivingData){
                            //停车通电算法处理
                            Object obj = drivingData.getStatisticData().get(ParkingPowerOn.class.getSimpleName());
                            if(obj instanceof VarParkingPowerOn){
                                VarParkingPowerOn var = (VarParkingPowerOn)obj;
                                long gpsTime = point.getGpsTime();

                                //上一个行程设置了停车时间
                                if(var.getParkingTime()>0 && gpsTime>var.getParkingTime()){
                                    int rotation = point.getRotation();
                                    int accStatus = point.getAccStatus();

                                    //ACC开且上次ACC也为开
                                    if (accStatus==1 && var.isLastAccStatus() && rotation<500) {
                                        long duration = gpsTime - var.getLastGpsTime();
                                        if (duration > 0) {
                                            var.setParkingPowerOnTime((int)(var.getParkingPowerOnTime() + duration));
                                        }
                                    }

                                    var.setLastGpsTime(gpsTime);
                                    var.setLastAccStatus(accStatus==1 && rotation<500);
                                }
                            }
                        }
                    }else if(TRIP_START.equals(tripStatusEnum) || TRIP_IN.equals(tripStatusEnum)){
                        //更新行程信息
                        preTripInfo = Optional.ofNullable(preTripInfo).orElse(handleStart(point));
                        preTripInfo.getPoints0200().add(point);
                        preTripInfo.getPoints0F37().add(point);

                        //更新行程状态判断信息
                        tripJudgeState = Optional.ofNullable(tripJudgeState).orElse(new TripJudgeState());
                        tripJudgeState.addCount0200(1);
                        Long lastGpsTime = tripJudgeState.getLastGpsTime();
                        tripJudgeState.setLowRotationDuration(null==lastGpsTime ? 0 : (tripJudgeState.getLastRotation()<=500 && point.getRotation()<=500 ? tripJudgeState.getLowRotationDuration()+(point.getGpsTime()-lastGpsTime) : 0)) ;
                        tripJudgeState.setLastGpsTime(point.getGpsTime());
                        tripJudgeState.setLastRotation(point.getRotation());
                    } else if(TRIP_FORCE_END.equals(tripStatusEnum) || TRIP_END.equals(tripStatusEnum)){
                        if(null!=tripJudgeState && null!=preTripInfo){
                            handleEnd(tripInfoList, preTripInfo, drivingData);
                        }

                        //清空状态
                        preTripInfo = null;
                        tripJudgeState = null;
                    }
                }

                //加入行程内的0F37点
                else if(ProtocolEnum.REALTIME.equals(point.getProtocol())){
                    if(null != preTripInfo){
                        preTripInfo.getPoints0F37().add(point);
                    }
                }
            }

            //强制结束没有结束的行程
            if(null!=tripJudgeState && null!=preTripInfo ){
                handleEnd(tripInfoList, preTripInfo, drivingData);
            }
        }

        //流计算只取第一个行程
       if(RunningTypeEnum.STREAM.equals(runningType) && tripInfoList.size()>0){
            tripInfoList = Collections.singletonList(tripInfoList.get(0));
        }

        return tripInfoList;
    }

    /**
     * 行程开始处理
     * @param point 0200位置点
     * @return 行程信息
     */
    default PreTripInfo handleStart(PointProtocol point){
        PreTripInfo preTripInfo = new PreTripInfo();
        preTripInfo.setStartTime(point.getGpsTime());
        preTripInfo.setStartLongitude(point.getOriginalLng());
        preTripInfo.setStartLatitude(point.getOriginalLat());
        preTripInfo.setStartHeight((int)point.getHeight());
        preTripInfo.setStartDirection(point.getDirection());
        return preTripInfo;
    }

    /**
     * 行程结束的处理
     * @param list 行程列表
     * @param preTripInfo 当前行程
     */
    default void handleEnd(List<PreTripInfo> list, PreTripInfo preTripInfo, DrivingData drivingData) {
        //最后一个0200点的gps时间（最大）
        List<PointProtocol> points0200 = preTripInfo.getPoints0200();
        if(CommonUtils.isCollectionNotEmpty(points0200) && points0200.size()>=TRIP_MIN_COUNT){
            PointProtocol last0200 = points0200.get(points0200.size() - 1);
            preTripInfo.setEndTime(last0200.getGpsTime());
            preTripInfo.setEndLatitude(last0200.getOriginalLat());
            preTripInfo.setEndLongitude(last0200.getOriginalLng());
            preTripInfo.setEndHeight((int)last0200.getHeight());
            preTripInfo.setEndDirection(last0200.getDirection());

            //精准里程油耗
            MileageAndFuel mileageAndFuel = TripStatisticUtils.calcMileageFuel(systemType(), points0200);
            preTripInfo.setPreciseFuel(ArithmeticUtils.fuelLToMl(mileageAndFuel.getStdFuelCon()));
            preTripInfo.setPreciseMileage(ArithmeticUtils.mileageKMToM(mileageAndFuel.getStdMileage()));

            //行程外统计信息处理，目前只有停车通电算法
            if(null != drivingData){
                VarParkingPowerOn varParkingPowerOn = new VarParkingPowerOn();
                varParkingPowerOn.setParkingTime(preTripInfo.getEndTime());
                drivingData.setStatisticData(ParkingPowerOn.class.getSimpleName(), varParkingPowerOn);
            }

            //加入行程列表
            list.add(preTripInfo);
        }
    }

    /**
     * 当前时间-上一个时间超过指定阀值，强制结束行程
     * @param lastGpsTime 上一个Point时间点
     * @param gpsTime     当前Point时间点
     * @return 是否强制结束
     */
    default boolean isForceEnd(long lastGpsTime, long gpsTime) {
        return (gpsTime - lastGpsTime) > FORCE_END_DURATION;
    }

    /**
     * 验证数据的是否合法
     * @param point 位置点
     * @param lastGpsTime 上个点时间，单位ms
     * @return 是否合法
     */
    boolean validate(PointProtocol point, long lastGpsTime);

}
